package org.acghub.mtdb.xmlgen.visitor.marshal;

import com.alibaba.fastjson2.JSONObject;
import org.acghub.mtdb.common.Bean;
import org.acghub.mtdb.common.ObjectUtil;
import org.acghub.mtdb.common.Type;
import org.acghub.mtdb.common.Var;
import org.acghub.mtdb.common.type.TypeBaseCollection;
import org.acghub.mtdb.common.type.TypeBaseMap;
import org.acghub.mtdb.common.type.TypeBasePrimitives;
import org.acghub.mtdb.common.type.TypeDateTime;
import org.acghub.mtdb.util.StringUtil;
import org.acghub.mtdb.xmlgen.visitor.VarMarshalVisitor;

import java.io.PrintStream;

/**
 * @author zhouhe
 */
public class FastJson2VarMarshalVisitor extends VarMarshalVisitor {
    public FastJson2VarMarshalVisitor(Var var, PrintStream ps, String prefix, boolean marshal) {
        super(var, ps, prefix, marshal);
    }

    @Override
    protected String getMarshaledClassName() {
        return JSONObject.class.getName();
    }

    @Override
    public void visit(TypeBasePrimitives type) {
        String varName = var.getName();
        if (marshal) {
            // marshal
            ps.println(prefix + "if (" + ObjectUtil.NON_NULL + "(this." + varName + ")) {");
            if (type instanceof TypeDateTime) {
                ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "writer.put(%s, %s.format(this.%s));\n", StringUtil.quote(varName), TypeDateTime.class.getName(), varName);
            } else {
                ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "writer.put(%s, this.%s);\n", StringUtil.quote(varName), varName);
            }
            ps.println(prefix + "}");
        } else {
            // unmarshal
            ps.println(prefix + "if (reader.containsKey(" + StringUtil.quote(varName) + ")) {");
            String javaType = var.getVarType().javaInterfaceType();
            if (var.getVarType() instanceof TypeDateTime) {
                ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "this.%s = %s.parse(reader.getString(%s));\n", varName, TypeDateTime.class.getName(), StringUtil.quote(varName));
            } else {
                ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "this.%s = reader.get%s(%s);\n", varName, javaType, StringUtil.quote(varName));
            }
            ps.println(prefix + "}");
        }
    }

    @Override
    public void visit(TypeBaseCollection type) {
//        //value: bean、primitives、datetime
        if (marshal) {
            marshalCollection(type);
        } else {
            unmarshalCollection(type);
        }
    }

    @Override
    public void visit(TypeBaseMap type) {
        //key: primitives、datetime
        //value: bean、primitives、datetime
        if (marshal) {
            marshalMap(type);
        } else {
            unmarshalMap(type);
        }
    }

    @Override
    public void visit(Bean bean) {
        String varName = var.getName();
        if (marshal) {
            ps.println(prefix + "if (" + ObjectUtil.NON_NULL + "(this." + varName + ")) {");
            ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "%s _writer_ = new %s();\n", getMarshaledClassName(), getMarshaledClassName());
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "this." + varName + ".marshal(_writer_);");
            ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "writer.put(%s, %s);\n", StringUtil.quote(varName), "_writer_");
            ps.println(prefix + "}");
        } else {
            ps.println(prefix + "if (reader.containsKey(" + StringUtil.quote(varName) + ")) {");
            ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "%s _reader_ = reader.getJSONObject(%s);\n", getMarshaledClassName(), StringUtil.quote(varName));
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "this." + varName + ".unmarshal(_reader_);");
            ps.println(prefix + "}");
        }
    }

    private void marshalCollection(TypeBaseCollection type) {
        Type valueType = type.getValueType();
        String varName = var.getName();
        String valueJavaType = valueType.javaInterfaceType();
        ps.println(prefix + "if (" + ObjectUtil.NON_NULL + "(this." + var.getName() + ")) {");
        if (valueType instanceof Bean) {
            ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "java.util.List<%s> _list_ = new java.util.ArrayList<>();\n", getMarshaledClassName());
            ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "for (%s _v_: this.%s) {\n", valueJavaType, varName);
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _writer_ = new %s();\n", getMarshaledClassName(), getMarshaledClassName());
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "_v_.marshal(_writer_);");
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "_list_.add(_writer_);");
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "}");
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "writer.put(" + StringUtil.quote(varName) + ", _list_);");
        } else if (valueType instanceof TypeDateTime) {
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "java.util.List<String> _list_ = new java.util.ArrayList<>();");
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "for (" + valueJavaType + " _v_: this." + varName + ") {");
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "_list_.add(%s.format(_v_));\n", TypeDateTime.class.getName());
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "}");
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "writer.put(" + StringUtil.quote(varName) + ", _list_);");
        } else if (valueType instanceof TypeBasePrimitives) {
            ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "writer.put(" + StringUtil.quote(varName) + ", this." + varName + ");");
        }
        ps.println(prefix + "}");
    }

    private void unmarshalCollection(TypeBaseCollection type) {
        Type valueType = type.getValueType();
        String varName = var.getName();
        String valueJavaType = valueType.javaInterfaceType();
        ps.printf(prefix + "if (reader.containsKey(%s)) {\n", StringUtil.quote(varName));
        if (valueType instanceof Bean) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(1)) + "java.util.List<%s> _list_ = reader.getList(%s, %s.class);\n", getMarshaledClassName(), StringUtil.quote(varName), getMarshaledClassName());
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(1)) + "for (%s _reader_ : _list_) {\n", getMarshaledClassName());
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _v_ = new %s(this, %s);\n", valueType.getName(), valueType.getName(), StringUtil.quote(varName));
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "_v_.unmarshal(_reader_);");
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "this." + varName + ".add(_v_);");
            ps.println(StringUtil.concat(prefix, StringUtil.tab(1)) + "}");
        } else if (valueType instanceof TypeDateTime) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(1)) + "java.util.List<String> _list_ = reader.getList(%s, String.class);\n", StringUtil.quote(varName));
            ps.println(StringUtil.concat(prefix, StringUtil.tab(1)) + "for (String _v_ : _list_) {");
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "this." + varName + ".add(" + TypeDateTime.class.getName() + ".parse(_v_));");
            ps.println(StringUtil.concat(prefix, StringUtil.tab(1)) + "}");
        } else if (valueType instanceof TypeBasePrimitives) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(1)) + "java.util.List<%s> _list_ = reader.getList(%s, %s.class);\n", valueJavaType, StringUtil.quote(varName), valueJavaType);
            ps.println(StringUtil.concat(prefix, StringUtil.tab(1)) + "this." + varName + ".addAll(_list_);");
        }
        ps.println(prefix + "}");
    }

    private void marshalMap(TypeBaseMap type) {
        String varName = var.getName();
        Type keyType = type.getKeyType();
        Type valueType = type.getValueType();
        String keyJavaType = keyType.javaInterfaceType();
        String valueJavaType = valueType.javaInterfaceType();
        ps.println(prefix + "if (" + ObjectUtil.NON_NULL + "(this." + varName + ")) {");
        ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "%s _writer_ = new %s();\n", getMarshaledClassName(), getMarshaledClassName());
        ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "for (java.util.Map.Entry<%s, %s> _entry_ : this.%s.entrySet()) {\n", keyJavaType, valueJavaType, varName);
        //key
        if (keyType instanceof TypeDateTime) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "String _key_ = %s.format(_entry_.getKey());\n", TypeDateTime.class.getName());
        } else {
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "String _key_ = String.valueOf(_entry_.getKey());");
        }

        //value
        if (valueType instanceof Bean) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _value_ = new %s();\n", getMarshaledClassName(), getMarshaledClassName());
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "_entry_.getValue().marshal(_value_);");
        } else if (valueType instanceof TypeDateTime) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "String _value_ = %s.format(_entry_.getValue());\n", TypeDateTime.class.getName());
        } else {
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + valueJavaType + " _value_ = _entry_.getValue();");
        }
        ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "_writer_.put(_key_, _value_);");
        ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "}");
        ps.printf(StringUtil.concat(prefix, StringUtil.tab(1)) + "writer.put(%s, _writer_);\n", StringUtil.quote(varName));
        ps.println(prefix + "}");
    }

    private void unmarshalMap(TypeBaseMap type) {
        String varName = var.getName();
        Type keyType = type.getKeyType();
        Type valueType = type.getValueType();
        String keyJavaType = keyType.javaInterfaceType();
        String valueJavaType = valueType.javaInterfaceType();
        ps.println(prefix + "if (reader.containsKey(" + StringUtil.quote(varName) + ")) {");
        ps.printf(StringUtil.concat(prefix, StringUtil.TAB) + "%s _map_reader_ = reader.getJSONObject(%s);\n", getMarshaledClassName(), StringUtil.quote(varName));
        ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "for (String _str_key_ : _map_reader_.keySet()) {");
        //key
        ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _key_ = %s(_str_key_, %s.class);\n", keyJavaType, ObjectUtil.CONVERT, keyJavaType);

        //value
        if (valueType instanceof Bean) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _reader_ = _map_reader_.getJSONObject(_str_key_);\n", getMarshaledClassName());
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _value_ = new %s(this, %s);\n", valueJavaType, valueJavaType, StringUtil.quote(varName));
            ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "_value_.unmarshal(_reader_);");
        } else if (valueType instanceof TypeDateTime) {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _value_ = %s.parse(_map_reader_.getString(_str_key_));\n", valueJavaType, TypeDateTime.class.getName());
        } else {
            ps.printf(StringUtil.concat(prefix, StringUtil.tab(2)) + "%s _value_ = _map_reader_.get%s(_str_key_);\n", valueJavaType, valueJavaType);
        }
        ps.println(StringUtil.concat(prefix, StringUtil.tab(2)) + "this." + varName + ".put(_key_, _value_);");
        ps.println(StringUtil.concat(prefix, StringUtil.TAB) + "}");
        ps.println(prefix + "}");
    }
}
